package com.purchase.service.impl;

import com.purchase.entity.*;
import com.purchase.mapper.*;
import com.purchase.service.PayService;
import com.purchase.util.*;
import com.purchase.util.wxpay.app.SignValidUtilAPP;
import com.purchase.util.wxpay.app.WxPayAESUtilAPP;
import com.purchase.util.wxpay.jsapi.SignValidUtil;
import com.purchase.util.wxpay.WXPayUtil;
import com.purchase.util.wxpay.app.WxpayHandlerAPP;
import com.purchase.util.wxpay.jsapi.WxPayAESUtil;
import com.purchase.util.wxpay.jsapi.WxpayHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.entity.Example.Criteria;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;

@Slf4j
@Service
public class PayServiceImpl implements PayService {

    @Autowired
    private WxpayHandler wxpayHandler;

    @Autowired
    private WxpayHandlerAPP wxpayHandlerAPP;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private PayMapper payMapper;

    @Autowired
    private GoodsStockMapper goodsStockMapper;

    @Autowired
    private WxUserMapper wxUserMapper;

    @Autowired
    private CouponMapper couponMapper;

    @Autowired
    private WxUserMemberMapper wxUserMemberMapper;

    @Autowired
    private GoodsMapper goodsMapper;

    @Autowired
    private ChargeMapper chargeMapper;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResponseForm doUnifiedOrder(RequestForm param) {
        log.info("发起下单 - 开始");
        ResponseForm result = new ResponseForm();
        Map<String, String> paramMap = (Map<String, String>) param.getData();
        String orderId = null;
        try {
            String wxUid = paramMap.get("wxuid");
            orderId = paramMap.get("orderId");
            String contacts = paramMap.get("contacts");
            String mobilePhone = paramMap.get("mobilePhone");
            String detailedAddress = paramMap.get("detailedAddress");
            String tradeType = paramMap.get("tradeType");
            String verifyPhone = paramMap.get("invitePhone");
            tradeType = null == tradeType ? "JSAPI" : tradeType;
            if (!StringUtils.isNotBlank(wxUid)
                    || !StringUtils.isNotBlank(orderId)
                    || !StringUtils.isNotBlank(contacts)
                    || !StringUtils.isNotBlank(mobilePhone)
                    || !StringUtils.isNotBlank(detailedAddress)
                    || !StringUtils.isNotBlank(tradeType)
                    ) {
                result.setStatus(false);
                result.setMessage(LogInfo.PARAM_ERROR);
                log.error(LogInfo.PARAM_ERROR);
                return result;
            }

            //校验库存
            List<Object> stockList = this.getStockList(orderId);
            if (stockList != null && stockList.size() != 0) {
                Map<String, Object> stockListMap = new HashMap<>();
                stockListMap.put("stockList", stockList);
                result.setData(stockListMap);
                return result;
            }

            //保存收货地址信息到订单
            orderMapper.saveOrderAddress(orderId, contacts, mobilePhone, detailedAddress);

            //购买人角色
            WxUser wxUser = new WxUser();
            wxUser.setWxUid(wxUid);
            wxUser = wxUserMapper.selectOne(wxUser);
            String openid = wxUser.getOpenId();

            WxUserMember payerRole = new WxUserMember();
            payerRole.setWxUid(wxUid);
            payerRole = wxUserMemberMapper.selectOne(payerRole);
            if (payerRole == null) {
                payerRole = new WxUserMember();
                payerRole.setRoleId(1);
                payerRole.setWxUid(wxUid);
            }

            //推荐人角色
            WxUserMember referrerRole = new WxUserMember();
            if (verifyPhone != null && !"".equals(verifyPhone)) {
                WxUser referrer = new WxUser();
                referrer.setVerifyPhone(verifyPhone);
                referrer = wxUserMapper.selectOne(referrer);
                referrerRole.setWxUid(referrer.getWxUid());
                referrerRole = wxUserMemberMapper.selectOne(referrerRole);
            } else {
                //如果 verifyPhone 为空，说明没有推荐人，则查询其是否有直属代购
                WxUserMember referrer = new WxUserMember();
                referrer.setWxUid(payerRole.getInviteWxUid());
                if (referrer.getWxUid() != null) {
                    referrerRole = wxUserMemberMapper.selectOne(referrer);
                }
            }

            //根据代购佣金规则保存代购返现数据
            this.saveCharge(orderId, payerRole, referrerRole);

            //组装支付信息
            Map<String, Object> payMap = new HashMap<>();
            Map<String, Object> payOrder = payMapper.getUPayOrderByOrderId(orderId);
            String id;
            if (payOrder != null) {
                payMapper.delPayOrderByOrderId(orderId);
            }

            //如果新的订单则生成新的流水
            Map<String, Object> order = orderMapper.getWaitPayOrderPriceByIdForPay(orderId);
            Map<String, Object> orderByIdAndStoreNo = orderMapper.getOrderByIdAndStoreNo(orderId, null);
            if ("Q".equals(orderByIdAndStoreNo.get("order_status"))) {
                result.setStatus(true);
                result.setCode("500");
                result.setMessage("订单已过期");
                log.error("订单已过期error,orderId=" + orderId);
                return result;
            }
            id = OrderIdGenerater.generatePayOrderId();
            payMap.put("id", id);
            payMap.put("orderId", orderId);
            payMap.put("totalFee", order.get("totalFee"));
            payMap.put("goodsBody", "叮叮仓" + orderId);
            payMap.put("openid", openid);
            payMap.put("tradeType", tradeType);

            //入库流水
            log.debug("入库流水" + id);
            payMapper.savePayOrder(payMap);

            //调用微信支付统一下单
            log.debug("调用微信支付统一下单");
            Map<String, String> map = null;
            if ("JSAPI".equals(tradeType)) {
                orderMapper.updateOrderTradeType(orderId, "JSAPI");
                map = wxpayHandler.doUnifiedOrder(payMap);
            } else if ("APP".equals(tradeType)) {
                orderMapper.updateOrderTradeType(orderId, "APP");
                map = wxpayHandlerAPP.doUnifiedOrder(payMap);
            }
            log.debug("微信支付统一下单返回:{}", map);

            HashMap<String, Object> updMap = new HashMap<>();
            updMap.put("prepayId", map.get("prepay_id"));
            updMap.put("id", id);
            payMapper.modifyPayOrder(updMap);
            result.setData(map);
        } catch (Exception e) {
            result.setStatus(false);
            result.setMessage(LogInfo.ERROR);
            log.error("支付下单error,orderId=" + orderId, e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return result;
    }

    private void saveCharge(String orderId, WxUserMember payerRole, WxUserMember referrerRole) throws Exception {
        try {
            //如果推荐人是代购
            if (referrerRole != null && referrerRole.getRoleId() != null && referrerRole.getRoleId() == 3) {
                BigDecimal charge = BigDecimal.ZERO;
                //如果购买人是普通用户
                if (payerRole.getRoleId() != null && payerRole.getRoleId() == 1) {
                    List<Map<String, Object>> orderDetails = orderMapper.getOrderDetailListByOrderId(orderId);
                    for (Map<String, Object> orderDetail : orderDetails) {
                        Goods good = new Goods();
                        good.setGoodsId(Integer.valueOf(String.valueOf(orderDetail.get("goods_id"))));
                        good = goodsMapper.selectOne(good);
                        BigDecimal num = new BigDecimal((int) orderDetail.get("goods_num"));
                        BigDecimal goodMemberPrice = good.getMemberPrice() == null ? good.getDiscountPrice() : good.getMemberPrice();
                        BigDecimal goodDiscountPrice = good.getDiscountPrice() == null ? BigDecimal.ZERO : good.getDiscountPrice();
                        charge = goodDiscountPrice.subtract(goodMemberPrice).multiply(num).add(charge);
                    }
                    //保存数据到代购返现表
                    Charge insCharge = new Charge();
                    insCharge.setCharge(charge);
                    insCharge.setInviteWxUid(referrerRole.getWxUid());
                    insCharge.setPayerWxUid(payerRole.getWxUid());
                    insCharge.setOrderId(orderId);
                    insCharge.setOrderStatus("W");
                    Charge dupCharge = chargeMapper.selectByPrimaryKey(insCharge);
                    if (dupCharge == null) {
                        chargeMapper.insertSelective(insCharge);
                    }
                }

                //如果购买人是会员或代购
                if (payerRole.getRoleId() != null && payerRole.getRoleId() == 2 || payerRole.getRoleId() == 3 && payerRole.getStatus() == 1) {
                    List<Map<String, Object>> orderDetails = orderMapper.getOrderDetailListByOrderId(orderId);
                    for (Map<String, Object> orderDetail : orderDetails) {
                        Goods good = new Goods();
                        good.setGoodsId(Integer.valueOf(String.valueOf(orderDetail.get("goods_id"))));
                        good = goodsMapper.selectOne(good);
                        BigDecimal num = new BigDecimal((int) orderDetail.get("goods_num"));
                        BigDecimal goodMemberPrice = good.getMemberPrice() == null ? good.getDiscountPrice() : good.getMemberPrice();
                        BigDecimal goodDiscountPrice = good.getDiscountPrice() == null ? BigDecimal.ZERO : good.getDiscountPrice();
                        charge = goodDiscountPrice.subtract(goodMemberPrice).multiply(new BigDecimal("0.25")).multiply(num).add(charge);
                    }

                    //查询购买人直属代购
                    WxUserMember payerMember = new WxUserMember();
                    payerMember.setWxUid(payerRole.getWxUid());
                    payerMember = wxUserMemberMapper.selectOne(payerMember);
                    if (payerMember.getInviteWxUid() != null) {
                        //如果购买人是会员且有直属代购，则给其直属代购佣金
                        //保存数据到代购返现表
                        Charge insCharge = new Charge();
                        insCharge.setCharge(charge);
                        insCharge.setInviteWxUid(payerMember.getInviteWxUid());
                        insCharge.setPayerWxUid(payerRole.getWxUid());
                        insCharge.setOrderId(orderId);
                        insCharge.setOrderStatus("W");
                        Charge dupCharge = chargeMapper.selectByPrimaryKey(insCharge);
                        if (dupCharge == null) {
                            chargeMapper.insertSelective(insCharge);
                        }
                    } else {
                        //如果购买人是会员且无直属代购，则给本次推荐人佣金
                        //保存数据到代购返现表
                        Charge insCharge = new Charge();
                        insCharge.setCharge(charge);
                        insCharge.setInviteWxUid(referrerRole.getWxUid());
                        insCharge.setPayerWxUid(payerRole.getWxUid());
                        insCharge.setOrderId(orderId);
                        insCharge.setOrderStatus("W");
                        Charge dupCharge = chargeMapper.selectByPrimaryKey(insCharge);
                        if (dupCharge == null) {
                            chargeMapper.insertSelective(insCharge);
                        }
                    }
                }

            }
        } catch (Exception e) {
            log.error("保存代购返现数据error,orderId=" + orderId, e);
            throw new Exception(e);
        }
    }

    private List<Object> getStockList(String orderId) {
        //查该笔订单内详情的库存
        List<Map<String, Object>> detailList = orderMapper.getOrderDetailListByOrderId(orderId);
        if (detailList == null) {
            log.error("查询库存error,订单" + orderId + "详情内库存为null");
            return null;
        }
        //如果商品已下架加到 stockList，另如果库存小于订单内详情数量则将此详情放入一个无货 stockList 并给前端返回 stockList
        List<Object> stockList = new ArrayList<>();
        for (Map<String, Object> detail : detailList) {
            int soldout = (int) detail.get("soldout");
            if (soldout == 1) {
                stockList.add(detail);
            } else if ((int) detail.get("stock_num") < (int) detail.get("goods_num")) {
                stockList.add(detail);
            }
        }
        return stockList;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String payNotify(HttpServletRequest request) {
        log.info("接收微信支付结果通知-开始");
        String resXml = "";
        Map resultMap = new HashMap();
        Map payOrderMap = new HashMap();
        Map updOrderMap = new HashMap();
        try {
            //接收http请求处理
            String notifyXml = HttpClient.reciveHttpReq(request, "微信支付结果通知");

            //xml转map
            Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyXml);
            log.debug("xml to map {}", notifyMap);
            String appid = notifyMap.get("appid");

            //验签
            if (appid.equals(PropertiesUtil.getValue(PropertiesUtil.WXPATH, "wxpay.appid"))) {
                SignValidUtil.isSignatureValid(notifyMap);
            } else if (appid.equals(PropertiesUtil.getValue(PropertiesUtil.WXPATH, "wxpay.appid.APP"))) {
                SignValidUtilAPP.isSignatureValid(notifyMap);
            }

            //解析微信xml
            String notifyResultCode = notifyMap.get("result_code");
            String notifyReturnCode = notifyMap.get("return_code");
            String notifyOutTradeNo = notifyMap.get("out_trade_no");
            String notifyTransactionId = notifyMap.get("transaction_id");
            String notifyReturnMsg = notifyMap.get("return_msg");
            String notifyEndTime = notifyMap.get("time_end");
            String notifyTotalFee = notifyMap.get("total_fee");
            log.debug("resultCode=" + notifyResultCode + ",returnCode=" + notifyReturnCode);

            payOrderMap.put("id", notifyOutTradeNo);
            payOrderMap.put("transactionId", notifyTransactionId);
            payOrderMap.put("endTime", notifyEndTime);
            payOrderMap.put("payFailDesc", notifyReturnMsg);

            //获取库里流水信息
            log.debug("获取库里流水信息");
            Map payOrder = payMapper.getPayOrderById(payOrderMap);
            String orderId = String.valueOf(payOrder.get("order_id"));
            String orderTotalFee = String.valueOf(payOrder.get("total_fee"));
            String payStatus = String.valueOf(payOrder.get("pay_status"));

            //获取库里订单信息
            log.debug("获取库里订单信息");
            Map<String, Object> order = orderMapper.getOrderByIdAndStoreNo(orderId, null);
            String orderStatus = String.valueOf(order.get("order_status"));

            //校验订单和流水状态：如果不是待支付W或支付失败则不允许更新状态
            if (!"W".equals(orderStatus) && !"F".equals(orderStatus)) {
                log.debug("订单" + orderId + "已支付");
                resultMap.put("return_msg", "OK");
                resultMap.put("return_code", "SUCCESS");
                resXml = WXPayUtil.mapToXml(resultMap);
                return resXml;
            }

            //更新流水、订单
            if ("SUCCESS".equals(notifyResultCode) && "SUCCESS".equals(notifyReturnCode)) {

                //更新流水为成功
                log.debug("更新流水" + notifyOutTradeNo + "为成功");
                payOrderMap.put("payStatus", "S");
                payMapper.modifyPayOrder(payOrderMap);

                //更新订单为成功
                updOrderMap.put("orderStatus", "S");
                updOrderMap.put("orderId", orderId);
                log.debug("更新订单" + orderId + "为成功");
                orderMapper.updateOrderStatusById(updOrderMap);

                //更新用户优惠券
                couponMapper.payCoupon(orderId);

                //更新代购返现数据
                Charge charge = new Charge();
                charge.setOrderStatus("S");
                Example example = new Example(Charge.class);
                Criteria criteria = example.createCriteria();
                criteria.andEqualTo("orderId", orderId);
                chargeMapper.updateByExampleSelective(charge, example);

                //校验返回的订单金额是否与商户侧的订单金额一致，防止数据泄漏导致出现“假通知”，造成资金损失
                log.debug("微信返回金额：" + notifyTotalFee);
                log.debug("系统流水金额：" + orderTotalFee);
                if (!notifyTotalFee.equals(yuan2fen(orderTotalFee))) {
                    log.error("系统流水金额：" + yuan2fen(orderTotalFee) + "与微信返回金额：" + notifyTotalFee + "不一致，流水号=" + notifyOutTradeNo);

                    //更新流水为失败
                    log.debug("更新流水" + notifyOutTradeNo + "为失败");
                    payOrderMap.put("payStatus", "F");
                    payOrderMap.put("payFailDesc", "系统流水金额：" + yuan2fen(orderTotalFee) + "与微信返回金额：" + notifyTotalFee + "不一致");
                    payMapper.modifyPayOrder(payOrderMap);

                    //更新订单为失败
                    log.debug("更新订单" + orderId + "为失败");
                    updOrderMap.put("orderStatus", "F");
                    updOrderMap.put("orderId", orderId);
                    orderMapper.updateOrderStatusById(updOrderMap);

                    //返回微信结果
                    resultMap.put("return_code", "FAIL");
                    resXml = WXPayUtil.mapToXml(resultMap);
                    return resXml;
                }
                log.debug("校验金额一致");
            } else {
                //更新流水为失败
                log.debug("更新流水为失败");
                payOrderMap.put("payStatus", "F");
                payOrderMap.put("payFailDesc", notifyReturnMsg);
                payMapper.modifyPayOrder(payOrderMap);

                //更新订单为失败
                log.debug("更新订单为失败");
                updOrderMap.put("orderStatus", "F");
                updOrderMap.put("orderId", orderId);
                orderMapper.updateOrderStatusById(updOrderMap);

                //还原库存（通过订单里的商品数量修改库存数量）
                restoreGoodsStock(orderId);
                log.debug("已还原库存，订单号=" + orderId);
            }

            // 给微信返回响应报文
            resultMap.put("return_msg", "OK");
            resultMap.put("return_code", "SUCCESS");

        } catch (Exception e) {
            log.error("接收微信支付结果通知error", e);
            resultMap.put("return_code", "FAIL");
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        try {
            resXml = WXPayUtil.mapToXml(resultMap);
        } catch (Exception e) {
            log.error("接收微信支付结果通知error", e);
        }
        log.info("接收微信支付结果通知-结束-{}", resXml);
        return resXml;
    }

    @Override
    public ResponseForm orderQuery(RequestForm param) {
        log.info("查询订单 - 开始");
        ResponseForm result = new ResponseForm();
        Map<String, Object> paramMap = (Map<String, Object>) param.getData();
        String orderId = null;
        Map<String, String> map = null;
        try {
            //调用微信支付查询订单
            log.debug("调用微信支付查询订单");
            orderId = String.valueOf(paramMap.get("orderId"));
            String tradeType = String.valueOf(paramMap.get("tradeType"));

            Map<String, Object> payOrderByOrderId = payMapper.getPayOrderByOrderId(orderId);
            paramMap.put("transaction_id", payOrderByOrderId.get("transaction_id"));
            paramMap.put("out_trade_no", payOrderByOrderId.get("id"));
            if ("JSAPI".equals(tradeType)) {
                map = wxpayHandler.orderQuery(paramMap);
            } else if ("APP".equals(tradeType)) {
                map = wxpayHandlerAPP.orderQuery(paramMap);
            }
            log.debug("微信支付查询订单返回:{}", map.toString());

            result.setData(map);
        } catch (Exception e) {
            result.setStatus(false);
            result.setMessage(LogInfo.ERROR);
            log.error("查询订单error,orderId=" + orderId, e);
        }
        return result;
    }

    private void restoreGoodsStock(String orderId) {
        List<Map<String, Object>> detailList = orderMapper.getOrderDetailListByOrderId(orderId);
        for (Map<String, Object> detail : detailList) {
            int goodsId = (int) detail.get("goods_id");
            String specsName = (String) detail.get("specs_name");
            int goodsNum = (int) detail.get("goods_num");

            //查询库存
            GoodsStock goodsStock = new GoodsStock();
            goodsStock.setGoodsId(goodsId);
            goodsStock.setSpecsName(specsName);
            List<GoodsStock> goodsStocks = goodsStockMapper.select(goodsStock);
            Integer stockNum = goodsStocks.get(0).getStockNum();

            //更新商品库存
            goodsStock.setStockNum(stockNum + goodsNum);
            Example example = new Example(GoodsStock.class);
            Criteria criteria = example.createCriteria();
            criteria.andEqualTo("goodsId", goodsId);
            criteria.andEqualTo("specsName", specsName);
            goodsStockMapper.updateByExampleSelective(goodsStock, example);
        }
    }

    private String yuan2fen(String amount) {
        String currency = amount.replaceAll("[$￥,]", "");
        int index = currency.indexOf(".");
        int length = currency.length();
        Long amLong;
        if (index == -1) {
            amLong = Long.valueOf(currency + "00");
        } else if (length - index >= 3) {
            amLong = Long.valueOf((currency.substring(0, index + 3)).replace(".", ""));
        } else if (length - index == 2) {
            amLong = Long.valueOf((currency.substring(0, index + 2)).replace(".", "") + 0);
        } else {
            amLong = Long.valueOf((currency.substring(0, index + 1)).replace(".", "") + "00");
        }
        return amLong.toString();
    }

}
